﻿using System;
using System.Collections;
using System.ComponentModel;
using System.IO;
using System.Messaging;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace MsgQueueTest.Message
{
    public class MyXmlMessageFormater : IMessageFormatter
    {
        // Fields
        private readonly Hashtable targetSerializerTable;
        private Encoding encoding;
        private string[] targetTypeNames;
        private Type[] targetTypes;
        private bool typeNamesAdded;
        private bool typesAdded;

        // Methods
        public MyXmlMessageFormater()
        {
            targetSerializerTable = new Hashtable();
            TargetTypes = new Type[0];
            TargetTypeNames = new string[0];
            encoding = Encoding.UTF8;
        }

        public MyXmlMessageFormater(string[] targetTypeNames)
            : this(targetTypeNames, Encoding.UTF8)
        {
        }

        public MyXmlMessageFormater(Type[] targetTypes)
            : this(targetTypes, Encoding.UTF8)
        {
        }

        public MyXmlMessageFormater(string[] targetTypeNames, Encoding encoding)
        {
            targetSerializerTable = new Hashtable();
            TargetTypeNames = targetTypeNames;
            TargetTypes = new Type[0];
            this.encoding = encoding;
        }

        public MyXmlMessageFormater(Type[] targetTypes, Encoding encoding)
        {
            targetSerializerTable = new Hashtable();
            TargetTypes = targetTypes;
            TargetTypeNames = new string[0];
            this.encoding = encoding;
        }

        [MessagingDescription("XmlMsgTargetTypeNames")]
        public string[] TargetTypeNames
        {
            get { return targetTypeNames; }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                typeNamesAdded = false;
                targetTypeNames = value;
            }
        }

        [Browsable(false), MessagingDescription("XmlMsgTargetTypes"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Type[] TargetTypes
        {
            get { return targetTypes; }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                typesAdded = false;
                targetTypes = value;
            }
        }

        #region IMessageFormatter Members

        public bool CanRead(System.Messaging.Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            CreateTargetSerializerTable();
            var streamReader = new StreamReader(message.BodyStream, encoding);
            var xmlReader = new XmlTextReader(streamReader);
            xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
            xmlReader.ProhibitDtd = true;
            bool flag = false;
            foreach (XmlSerializer serializer in targetSerializerTable.Values)
            {
                if (serializer.CanDeserialize(xmlReader))
                {
                    flag = true;
                    break;
                }
            }
            message.BodyStream.Position = 0L;
            streamReader.Close();
            return flag;
        }

        public object Clone()
        {
            var formatter = new MyXmlMessageFormater();
            formatter.targetTypes = targetTypes;
            formatter.targetTypeNames = targetTypeNames;
            formatter.typesAdded = typesAdded;
            formatter.typeNamesAdded = typeNamesAdded;
            formatter.encoding = encoding;
            foreach (Type type in targetSerializerTable.Keys)
            {
                formatter.targetSerializerTable[type] = new XmlSerializer(type);
            }
            return formatter;
        }

        public object Read(System.Messaging.Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            CreateTargetSerializerTable();
            var streamReader = new StreamReader(message.BodyStream, encoding);
            var xmlReader = new XmlTextReader(streamReader);
            xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
            xmlReader.ProhibitDtd = true;
            foreach (XmlSerializer serializer in targetSerializerTable.Values)
            {
                if (serializer.CanDeserialize(xmlReader))
                {
                    return serializer.Deserialize(xmlReader);
                }
            }
            streamReader.Close();
            throw new InvalidOperationException("InvalidTypeDeserialization");
        }

        public void Write(System.Messaging.Message message, object obj)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }
            Stream stream = new MemoryStream();
            var streamWriter = new StreamWriter(stream, encoding);
            Type key = obj.GetType();
            XmlSerializer serializer = null;
            if (targetSerializerTable.ContainsKey(key))
            {
                serializer = (XmlSerializer) targetSerializerTable[key];
            }
            else
            {
                serializer = new XmlSerializer(key);
                targetSerializerTable[key] = serializer;
            }
            serializer.Serialize(streamWriter, obj);
            streamWriter.Flush();
            message.BodyStream = stream;
            message.BodyType = 0;
        }

        #endregion

        private void CreateTargetSerializerTable()
        {
            if (!typeNamesAdded)
            {
                for (int i = 0; i < targetTypeNames.Length; i++)
                {
                    Type type = Type.GetType(targetTypeNames[i], true);
                    if (type != null)
                    {
                        targetSerializerTable[type] = new XmlSerializer(type);
                    }
                }
                typeNamesAdded = true;
            }
            if (!typesAdded)
            {
                for (int j = 0; j < targetTypes.Length; j++)
                {
                    targetSerializerTable[targetTypes[j]] = new XmlSerializer(targetTypes[j]);
                }
                typesAdded = true;
            }
            if (targetSerializerTable.Count == 0)
            {
                throw new InvalidOperationException("TypeListMissing");
            }
        }

        // Properties
    }
}